package cn.dansj.configure;

import cn.dansj.bean.CurrentThreadLocalUserInfo;
import cn.dansj.annotation.logging.LogRecordParams;
import cn.dansj.common.request.configure.LoggerRecord;
import cn.dansj.common.utils.exception.PageNotFoundException;
import cn.dansj.common.utils.http.UrlUtils;
import cn.dansj.exception.InterfaceResponseException;
import cn.dansj.entity.RequestLog;
import cn.dansj.enums.GlobalEnv;
import cn.dansj.service.RequestLogService;
import cn.dansj.common.utils.json.DictMap;
import cn.dansj.bean.InterfaceResponse;
import cn.dansj.common.utils.bean.ResponseMessage;
import cn.dansj.common.utils.bean.Return;
import cn.dansj.common.utils.enums.ResponseCode;
import cn.dansj.common.request.RequestUtils;
import cn.dansj.common.utils.transfer.Transformation;
import cn.dansj.common.utils.transfer.RandomUtils;
import cn.dansj.common.utils.transfer.Verification;
import cn.dansj.utils.project.ProjectUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import jakarta.servlet.http.HttpServletRequest;

import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.List;

@Configuration
public class LogRecord implements LoggerRecord {
    private final RequestLogService requestLogService;
    private final Logger logger = LoggerFactory.getLogger(LogRecord.class);

    public LogRecord(RequestLogService requestLogService) {
        this.requestLogService = requestLogService;
    }

    @Override
    public Object execute(ProceedingJoinPoint point) throws Throwable {
        Signature signature = point.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        // 检查是否记录日志，不记录日志直接跳过，返回结果
        final Method method = methodSignature.getMethod();
        HttpServletRequest request = RequestUtils.getRequest();

        final LogRecordParams logRecordParams = method.getAnnotation(LogRecordParams.class);
        RequestLog requestLog = new RequestLog();
        requestLog.setResponseStartTime(new Timestamp(System.currentTimeMillis()));

        try {
            Object proceed = point.proceed();
            // 错误信息，完整的报错内容
            String exceptionMsg;
            // 错误概况
            String sketch;
            // 状态 1成功 0失败
            int status;
            // 状态响应码
            String responseCode;
            // 全局响应信息
            final ResponseMessage responseMessage = RequestUtils.getResponseMessage();

            //直接用的response返回的错误信息,不在异常捕捉中
            if (responseMessage != null) {
                responseCode = String.valueOf(responseMessage.getResponseCode());
                status = Transformation.transBooleanToInt(responseMessage.getStatus());
                exceptionMsg = responseMessage.getMessage();
                sketch = responseMessage.getMessage();
            } else if (proceed instanceof InterfaceResponse result) {
                //有返回值时状态强制为true
                if (result.getData() != null) {
                    result.setStatus(true);
                }
                status = Transformation.transBooleanToInt(result.getStatus());
                responseCode = String.valueOf(result.getResponseCode());
                exceptionMsg = result.getMessage();
                if (result.getStatus()) {
                    Object resultBody = result.getData();
                    //结果为列表时,最多保存5条记录
                    if (resultBody instanceof List<?> innerBody) {
                        sketch = DictMap.toJSONString(innerBody.size() > 5 ? innerBody.subList(0, 5) : innerBody);
                    } else {
                        //dict直接保存
                        sketch = DictMap.toJSONString(resultBody);
                    }
                } else {
                    sketch = exceptionMsg;
                }
            } else if (proceed instanceof DictMap result) {
                final Object s = result.get("status");
                status = s == null ? 1 : s instanceof Boolean p ? Transformation.transBooleanToInt(p) : 1;
                responseCode = status == 1 ? "200" : "500";
                exceptionMsg = result.get("message") != null ? result.get("message").toString() : status == 1 ? "查询成功" : "查询异常";
                if (status == 1) {
                    sketch = result.toString();
                } else {
                    sketch = exceptionMsg;
                }
            } else if (proceed instanceof Return<?> result) {
                status = Transformation.transBooleanToInt(result.getStatus());
                responseCode = String.valueOf(result.getResponseCode());
                exceptionMsg = result.getMessage();
                sketch = result.getStatus() ? "查询成功" : exceptionMsg;
            } else {
                status = 1;
                exceptionMsg = "";
                sketch = "";
                responseCode = "200";
            }
            requestLog.setStatus(status);
            requestLog.setResponseCode(responseCode);
            requestLog.setFullException(exceptionMsg);
            requestLog.setSketch(sketch);
            requestLog.setResponseEndTime(new Timestamp(System.currentTimeMillis()));
            // 其他部分的404，基本是接口模块部分的
            if ("404".equals(responseCode) && responseMessage == null) {
                throw new PageNotFoundException();
            } else {
                return proceed;
            }
        } catch (Throwable exception) {
            requestLog.setStatus(0);
            requestLog.setResponseEndTime(new Timestamp(System.currentTimeMillis()));
            Throwable baseException = Transformation.getBaseException(exception);

            // 记录完异常信息后统一抛出异常，由全局异常捕获器处理
            if (baseException instanceof InterfaceResponseException interfaceResponseException) {
                requestLog.setResponseCode(String.valueOf(interfaceResponseException.getErrorCode()));
                String fullException = ExceptionUtils.getStackTrace(interfaceResponseException);
                String errMsg = Transformation.nvl(interfaceResponseException.getMessage(), exception.toString());
                requestLog.setFullException(fullException);
                requestLog.setSketch(errMsg);
            } else {
                String fullException = ExceptionUtils.getStackTrace(exception);
                String errMsg = Transformation.nvl(baseException.getMessage(), exception.toString());
                requestLog.setResponseCode(String.valueOf(ResponseCode.RunTimeError.getStatusCode()));
                requestLog.setFullException(fullException);
                requestLog.setSketch(errMsg);
            }

            throw baseException;
        } finally {
            final boolean logEnable = ProjectUtils.getSettingByRedis("system", RequestUtils.isInternalForward() ? "log.interface.enable" : "log.system.enable", false);
            if (logEnable) {
                // /bindUserIdAndToken请求过来的是手机端的userAgent,需要获取到第一步获取二维码序列号时的userAgent
                String sourceUserAgent = request.getRequestURI().equals("/bindUserIdAndToken") ? Transformation.toString(ProjectUtils.getRedisUtils().hget(GlobalEnv.QRCodePrefix + RequestUtils.getRequestParam("sequence"), "userAgent")) : "";
                //获取请求controller的路径
                String controller = Verification.checkNotNull(logRecordParams) ? logRecordParams.controller() : methodSignature.getDeclaringTypeName();
                String interfaceUrl = RequestUtils.fromDispatcher();
                requestLog.setMethod(request.getMethod());
                requestLog.setUrl(Transformation.nvl(interfaceUrl, request.getRequestURI()));
                requestLog.setController(controller);
                requestLog.setSequence(RandomUtils.sequence());
                requestLog.setApiType(Transformation.parseInt(request.getAttribute("apiType"), 9));
                DictMap params = RequestUtils.getMaybeMultipartRequestParams();
                requestLog.setParams(params);
                requestLog.setUserAgent(request.getHeader("user-agent") + (Verification.checkNotNull(sourceUserAgent) ? "|" + sourceUserAgent : ""));
                String token = Transformation.nvl(request.getHeader("token"), params.getString("token"), "");
                requestLog.setToken(token);
                requestLog.setLoginUser(Transformation.nvl(CurrentThreadLocalUserInfo.getCurrentThreadLocalUserInfo().getCurrentUser().getUsername(), params.getString("username"), Transformation.toString(request.getAttribute("currentUsername"))));
                requestLog.setReferer(Transformation.lambdaTryCatch(() -> UrlUtils.getFullHost(request.getHeader("Referer")), () -> ""));
                requestLog.setCrtTime(new Timestamp(System.currentTimeMillis()));
                requestLog.setCost(requestLog.getResponseEndTime().getTime() - requestLog.getResponseStartTime().getTime());
                requestLog.setIpInfo(ProjectUtils.getRealIpInfo().toJSONString());
                GlobalEnv.threadPool.execute(() -> Transformation.lambdaTryCatch(() -> requestLogService.save(requestLog), (exception) -> logger.error("save database failed\n{}", DictMap.toJSONString(requestLog), exception)));
            }
        }
    }
}